﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using 历史记录.BusinessLogicLayer;
using 历史记录.Helper;

namespace 历史记录
{
    /// <summary>
    /// 一个分库分表的历史记录库 以日期分表每天一个表
    /// </summary>
    public class History<T> : History, IDisposable
      where T : Model, new()
    {
        /// <summary>
        /// 数据缓冲中转队列
        /// </summary>
        private BlockingCollection<T> Queue { get; }

        /// <summary>
        /// 数据缓冲长度 大于等于次长度进行保存
        /// </summary>
        public int Lengh { get; }

        /// <summary>
        /// 数据储存时间 超出此时间进行保存
        /// </summary>
        public TimeSpan Timeout { get; }

        public IBLL<T> DB { get; }

        internal History(IBLL<T> db, int len, TimeSpan timeout)
        {
            DB = db;
            Lengh = len;
            Timeout = timeout;
            Queue = new BlockingCollection<T>();
        }

        /// <summary>
        /// 写记录线程
        /// </summary>
        private void ThreadStart()
        {
            List<T> datas = new List<T>();
            while (true)//获取队列数据
            {
                bool take = Queue.TryTake(out T data, Timeout);//是否成功取到数据
                var len = datas.Count;//获取缓冲区长度
                if (len > 0)
                {
                    bool up = false;
                    if (data != null)
                        up = DB.UpDate(data.Id, datas); // 判断数据是否隔天
                    if (up || !take || len >= Lengh)//判断数据是否超时 超长
                    {
                        if (!up)
                            DB.Insertable(datas);//插入记录数据
                        datas.Clear();//清空缓冲列表
                    }
                }
                if (data != null)//获取数据不为空
                    datas.Add(data);//添加数据进入缓冲区
                else if (take) break;//获取数据为空  且 获取成功  跳出循环
            }
            datas.Add(new T { Id = DateTime.Now, State = WriteType.End });
            DB.Insertable(datas);//插入记录数据
            DB.Dispose();
        }

        /// <summary>
        /// 查询多日期
        /// </summary>
        /// <typeparam name="TResult">返回值类型</typeparam>
        /// <param name="start">查询开始时间</param>
        /// <param name="end">查询结束时间</param>
        /// <param name="Select">返回类型转换函数</param>
        /// <param name="Where">查询条件</param>
        /// <returns>查询结果</returns>
        public IEnumerable<IEnumerable<TResult>> Queryable<TResult>(DateTime start, DateTime end, Expression<Func<T, TResult>> Select, int interval = 0, Expression<Func<T, bool>> Where = null) where TResult : new()
        {
            if (interval > 1)
            {
                Expression<Func<T, bool>> think = a => a.Filtrate % interval == 0;
                Where = think.AndAlso(Where);
            }
            return DB.Queryable(start, end, Select, Where);
        }

        /// <summary>
        /// 查询 单日期
        /// </summary>
        /// <typeparam name="TResult">返回类型</typeparam>
        /// <param name="date">查日期</param>
        /// <param name="Select">转换类型函数</param>
        /// <param name="Where">查询条件判断条件函数</param>
        /// <returns>查询结果</returns>
        public IEnumerable<TResult> Queryable<TResult>(DateTime date, Expression<Func<T, TResult>> Select, int interval = 0, Expression<Func<T, bool>> Where = null) where TResult : new()
        {
            if (interval > 1)
            {
                Expression<Func<T, bool>> think = a => a.Filtrate % interval == 0;
                Where = think.AndAlso(Where);
            }
            var start = date.Date;
            var end = start.AddHours(24);
            return DB.Queryable(start, end, Select, Where).FirstOrDefault();
        }

        /// <summary>
        /// 查询多日期
        /// </summary>

        /// <param name="start">查询开始时间</param>
        /// <param name="end">查询结束时间</param>
        /// <param name="Where">查询条件</param>
        /// <returns>查询结果</returns>
        public IEnumerable<IEnumerable<T>> Queryable(DateTime start, DateTime end, int interval = 0, Expression<Func<T, bool>> Where = null)
        {
            if (interval > 1)
            {
                Expression<Func<T, bool>> think = a => a.Filtrate % interval == 0;
                Where = think.AndAlso(Where);
            }
            return DB.Queryable(start, end, Where);
        }

        /// <summary>
        /// 查询 单日期
        /// </summary>
        /// <param name="date">查日期</param>
        /// <param name="Where">查询条件判断条件函数</param>
        /// <returns>查询结果</returns>
        public IEnumerable<T> Queryable(DateTime date, int interval = 0, Expression<Func<T, bool>> Where = null)
        {
            if (interval > 1)
            {
                Expression<Func<T, bool>> think = a => a.Filtrate % interval == 0;
                Where = think.AndAlso(Where);
            }
            var start = date.Date;
            var end = start.AddHours(24);
            return DB.Queryable(start, end, Where).FirstOrDefault();
        }

        public uint FiltrateId { get; set; }

        /// <summary>
        /// 尝试在指定的时间段内将指定的项添加到緩存队列
        /// </summary>
        /// <param name="item">要添加到集合的项</param>
        /// <param name="millisecondsTimeout">等待的毫秒数，或为 System.Threading.Timeout.Infinite (-1)，表示无限期等待。</param>
        /// <returns> 如果在指定的时间内可以将 item 添加到集合中，则为 true；否则为 false。 如果该项有重复，并且基础集合不接受重复项，则会引发 System.InvalidOperationException。</returns>

        public bool Add(T item, int millisecondsTimeout = -1)
        {
            //状态为结束  或 数据为空 返回
            if (IsStop || item is null) return false;
            if (millisecondsTimeout < -1) millisecondsTimeout = -1;
            if (++FiltrateId >= int.MaxValue) FiltrateId = 0;
            item.Filtrate = FiltrateId;
            if (Interlocked.CompareExchange(ref run, 1, 0) == 0)
            {
                Queue.Add(new T { Id = DateTime.Now, State = WriteType.Start });
                Thread thread = new Thread(ThreadStart)
                {
                    IsBackground = true
                };
                thread.Start();//启动前台线程检测等待执行投递任务
            }
            if (item.Id == DateTime.MinValue) item.Id = DateTime.Now;
            return Queue.TryAdd(item, millisecondsTimeout);
        }

        /// <summary>
        ///     启动写入线程
        /// </summary>
        private int run;

        /// <summary>
        /// 当前记录线程工作是否已停止. true 已停止工作 ,false 正在工作.
        /// </summary>
        public bool IsStop
        { get; private set; }

        protected override void OnDispose()
        {
            IsStop = true;
            Queue.Add(null);
        }
    }

    public abstract class History : DisposableBase
    {
        private static List<History> Historys { get; } = new List<History>();

        /// <summary>
        /// 关闭所有记录任务线程
        /// </summary>
        public static void StopAll()
        {
            foreach (var item in Historys) item.Dispose();
        }

        public History()
        {
            Historys.Add(this);
        }
    }
}